﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using l1cs.server.datatables;
using l1cs.server.templates;
using System.Collections;

namespace l1cs.server.model
{
    public class L1Inventory : L1Object
    {
        private static Logger _log = Logger.getLogger( typeof( L1Inventory ).FullName );

        /// <summary>
        /// スレッドセーフなアイテムリスト(L1ItemInstance)
        /// </summary>
        protected ArrayList _items = ArrayList.Synchronized( new ArrayList() );

        public const int MAX_AMOUNT = 2000000000; // 2G

        public const int MAX_WEIGHT = 1500;

        public L1Inventory() { }

        /// <summary>
        /// インベントリ内のアイテムの総数
        /// </summary>
        /// <returns>アイテム総数</returns>
        public int getSize()
        {
            return _items.Count;
        }

        /// <summary>
        /// インベントリ内の全てのアイテム
        /// </summary>
        /// <returns></returns>
        public object[] getItems()
        {
            return _items.ToArray();
        }

        /// <summary>
        /// インベントリ内の総重量
        /// </summary>
        /// <returns></returns>
        public int getWeight()
        {
            int weight = 0;

            foreach ( L1ItemInstance item in _items )
            {
                weight += item.getWeight();
            }
            return weight;
        }

        /// <summary>
        /// 引数のアイテムを追加しても容量と重量が大丈夫か確認
        /// </summary>
        public const int OK = 0;

        public const int SIZE_OVER = 1;

        public const int WEIGHT_OVER = 2;

        public const int AMOUNT_OVER = 3;


        public virtual int checkAddItem( L1ItemInstance item, int count )
        {
            if ( item == null )
            {
                return -1;
            }
            if ( item.getCount() <= 0 || count <= 0 )
            {
                return -1;
            }
            if ( getSize() > Config.MAX_NPC_ITEM
                    || ( getSize() == Config.MAX_NPC_ITEM && ( !item.isStackable() || !checkItem( item
                            .getItem().getItemId() ) ) ) )
            { // 容量確認
                return SIZE_OVER;
            }

            int weight = getWeight() + item.getItem().getWeight() * count / 1000
                    + 1;
            if ( weight < 0 || ( item.getItem().getWeight() * count / 1000 ) < 0 )
            {
                return WEIGHT_OVER;
            }
            if ( weight > ( MAX_WEIGHT * Config.RATE_WEIGHT_LIMIT_PET ) )
            { // その他の重量確認（主にサモンとペット）
                return WEIGHT_OVER;
            }

            L1ItemInstance itemExist = findItemId( item.getItemId() );
            if ( itemExist != null && ( itemExist.getCount() + count ) > MAX_AMOUNT )
            {
                return AMOUNT_OVER;
            }

            return OK;
        }

        /// <summary>
        /// 引数のアイテムを追加しても倉庫の容量が大丈夫か確認
        /// </summary>
        public const int WAREHOUSE_TYPE_PERSONAL = 0;

        public const int WAREHOUSE_TYPE_CLAN = 1;

        public int checkAddItemToWarehouse( L1ItemInstance item, int count, int type )
        {
            if ( item == null )
            {
                return -1;
            }
            if ( item.getCount() <= 0 || count <= 0 )
            {
                return -1;
            }

            int maxSize = 100;
            if ( type == WAREHOUSE_TYPE_PERSONAL )
            {
                maxSize = Config.MAX_PERSONAL_WAREHOUSE_ITEM;
            }
            else if ( type == WAREHOUSE_TYPE_CLAN )
            {
                maxSize = Config.MAX_CLAN_WAREHOUSE_ITEM;
            }
            if ( getSize() > maxSize
                    || ( getSize() == maxSize && ( !item.isStackable() || !checkItem( item
                            .getItem().getItemId() ) ) ) )
            { // 容量確認
                return SIZE_OVER;
            }

            return OK;
        }

        /// <summary>
        /// 新しいアイテムの格納
        /// </summary>
        /// <param name="id"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public L1ItemInstance storeItem( int id, int count )
        {
            lock ( this )
            {
                if ( count <= 0 )
                {
                    return null;
                }
                L1Item temp = ItemTable.getInstance().getTemplate( id );
                if ( temp == null )
                {
                    return null;
                }

                if ( temp.isStackable() )
                {
                    L1ItemInstance item = new L1ItemInstance( temp, count );

                    if ( findItemId( id ) == null )
                    { // 新しく生成する必要がある場合のみIDの発行とL1Worldへの登録を行う
                        item.setId( IdFactory.getInstance().nextId() );
                        L1World.getInstance().storeObject( item );
                    }

                    return storeItem( item );
                }

                // スタックできないアイテムの場合
                L1ItemInstance result = null;
                for ( int i = 0; i < count; i++ )
                {
                    L1ItemInstance item = new L1ItemInstance( temp, 1 );
                    item.setId( IdFactory.getInstance().nextId() );
                    L1World.getInstance().storeObject( item );
                    storeItem( item );
                    result = item;
                }
                // 最後に作ったアイテムを返す。配列を戻すようにメソッド定義を変更したほうが良いかもしれない。
                return result;
            }
        }

        /// <summary>
        ///  DROP、購入、GMコマンドで入手した新しいアイテムの格納
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public L1ItemInstance storeItem( L1ItemInstance item )
        {
            lock ( this )
            {
                if ( item.getCount() <= 0 )
                {
                    return null;
                }
                int itemId = item.getItem().getItemId();
                if ( item.isStackable() )
                {
                    L1ItemInstance findItem = findItemId( itemId );
                    if ( findItem != null )
                    {
                        findItem.setCount( findItem.getCount() + item.getCount() );
                        updateItem( findItem );
                        return findItem;
                    }
                }
                item.setX( getX() );
                item.setY( getY() );
                item.setMap( getMapId() );
                int chargeCount = item.getItem().getMaxChargeCount();
                if ( itemId == 40006 || itemId == 40007 || itemId == 40008
                        || itemId == 140006 || itemId == 140008 || itemId == 41401 )
                {
                    Random random = new Random();
                    chargeCount -= random.Next( 5 );
                }
                if ( itemId == 20383 )
                {
                    chargeCount = 50;
                }
                item.setChargeCount( chargeCount );
                if ( item.getItem().getType2() == 0 && item.getItem().getType() == 2 )
                { // light系アイテム
                    item.setRemainingTime( item.getItem().getLightFuel() );
                }
                else
                {
                    item.setRemainingTime( item.getItem().getMaxUseTime() );
                }
                item.setBless( item.getItem().getBless() );
                _items.Add( item );
                insertItem( item );
                return item;
            }
        }

        /// <summary>
        /// trade、倉庫から入手したアイテムの格納
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public L1ItemInstance storeTradeItem( L1ItemInstance item )
        {
            lock ( this )
            {
                if ( item.isStackable() )
                {
                    L1ItemInstance findItem = findItemId( item.getItem().getItemId() );
                    if ( findItem != null )
                    {
                        findItem.setCount( findItem.getCount() + item.getCount() );
                        updateItem( findItem );
                        return findItem;
                    }
                }
                item.setX( getX() );
                item.setY( getY() );
                item.setMap( getMapId() );
                _items.Add( item );
                insertItem( item );
                return item;
            }
        }

        /// <summary>
        /// インベントリから指定されたアイテムIDのアイテムを削除する。
        /// </summary>
        /// <param name="itemid">削除するアイテムのitemid(objidではない)</param>
        /// <param name="count">削除する個数</param>
        /// <returns>実際に削除された場合はtrueを返す。</returns>
        /// <remarks>
        /// L1ItemInstanceへの参照がある場合はremoveItemの方を使用するのがよい。
        /// （こちらは矢とか魔石とか特定のアイテムを消費させるときに使う）
        /// </remarks>
        public bool consumeItem( int itemid, int count )
        {
            if ( count <= 0 )
            {
                return false;
            }
            if ( ItemTable.getInstance().getTemplate( itemid ).isStackable() )
            {
                L1ItemInstance item = findItemId( itemid );
                if ( item != null && item.getCount() >= count )
                {
                    removeItem( item, count );
                    return true;
                }
            }
            else
            {
                L1ItemInstance[] itemList = findItemsId( itemid );
                if ( itemList.Length == count )
                {
                    for ( int i = 0; i < count; i++ )
                    {
                        removeItem( itemList[i], 1 );
                    }
                    return true;
                }
                else if ( itemList.Length > count )
                {
                    // 指定個数より多く所持している場合
                    IComparer dc = new DataComparator();
                    Array.Sort( itemList, dc ); // エンチャント順にソートし、エンチャント数の少ないものから消費させる
                    for ( int i = 0; i < count; i++ )
                    {
                        removeItem( itemList[i], 1 );
                    }
                    return true;
                }
            }
            return false;
        }

        public class DataComparator : IComparer
        {
            public int Compare( Object item1, Object item2 )
            {
                return ( (L1ItemInstance)item1 ).getEnchantLevel()
                        - ( (L1ItemInstance)item2 ).getEnchantLevel();
            }
        }

        /// <summary>
        ///  指定したアイテムから指定個数を削除（使ったりゴミ箱に捨てられたとき）
        /// </summary>
        /// <param name="objectId"></param>
        /// <param name="count"></param>
        /// <returns>実際に削除した数</returns>
        public int removeItem( int objectId, int count )
        {
            L1ItemInstance item = getItem( objectId );
            return removeItem( item, count );
        }

        public int removeItem( L1ItemInstance item )
        {
            return removeItem( item, item.getCount() );
        }

        public int removeItem( L1ItemInstance item, int count )
        {
            if ( item == null )
            {
                return 0;
            }
            if ( item.getCount() <= 0 || count <= 0 )
            {
                return 0;
            }
            if ( item.getCount() < count )
            {
                count = item.getCount();
            }
            if ( item.getCount() == count )
            {
                int itemId = item.getItem().getItemId();
                if ( itemId == 40314 || itemId == 40316 )
                { // ペットのアミュレット
                    PetTable.getInstance().deletePet( item.getId() );
                }
                else if ( itemId >= 49016 && itemId <= 49025 )
                { // 便箋
                    LetterTable.deleteLetter( item.getId() );
                }
                else if ( itemId >= 41383 && itemId <= 41400 )
                { // 家具
                    foreach ( L1Object l1object in L1World.getInstance().getObject() )
                    {
                        if ( l1object is L1FurnitureInstance )
                        {
                            L1FurnitureInstance furniture = (L1FurnitureInstance)l1object;
                            if ( furniture.getItemObjId() == item.getId() )
                            { // 既に引き出している家具
                                FurnitureSpawnTable.getInstance().deleteFurniture( furniture );
                            }
                        }
                    }
                }
                deleteItem( item );
                L1World.getInstance().removeObject( item );
            }
            else
            {
                item.setCount( item.getCount() - count );
                updateItem( item );
            }
            return count;
        }

        /// <summary>
        /// _itemsから指定オブジェクトを削除
        /// (L1PcInstance、L1DwarfInstance、L1GroundInstanceでこの部分をオーバライドする)
        /// </summary>
        /// <param name="item"></param>
        public virtual void deleteItem( L1ItemInstance item )
        {
            _items.Remove( item );
        }

        /// <summary>
        /// 引数のインベントリにアイテムを移譲
        /// </summary>
        /// <param name="objectId"></param>
        /// <param name="count"></param>
        /// <param name="inventory"></param>
        /// <returns></returns>
        public L1ItemInstance tradeItem( int objectId, int count, L1Inventory inventory )
        {
            lock ( this )
            {
                L1ItemInstance item = getItem( objectId );
                return tradeItem( item, count, inventory );
            }
        }

        public L1ItemInstance tradeItem( L1ItemInstance item, int count, L1Inventory inventory )
        {
            lock ( this )
            {
                if ( item == null )
                {
                    return null;
                }
                if ( item.getCount() <= 0 || count <= 0 )
                {
                    return null;
                }
                if ( item.isEquipped() )
                {
                    return null;
                }
                if ( !checkItem( item.getItem().getItemId(), count ) )
                {
                    return null;
                }
                L1ItemInstance carryItem;
                if ( item.getCount() <= count )
                {
                    deleteItem( item );
                    carryItem = item;
                }
                else
                {
                    item.setCount( item.getCount() - count );
                    updateItem( item );
                    carryItem = ItemTable.getInstance().createItem( item.getItem().getItemId() );
                    carryItem.setCount( count );
                    carryItem.setEnchantLevel( item.getEnchantLevel() );
                    carryItem.setIdentified( item.isIdentified() );
                    carryItem.set_durability( item.get_durability() );
                    carryItem.setChargeCount( item.getChargeCount() );
                    carryItem.setRemainingTime( item.getRemainingTime() );
                    carryItem.setLastUsed( item.getLastUsed() );
                    carryItem.setBless( item.getBless() );
                }
                return inventory.storeTradeItem( carryItem );
            }
        }

        /// <summary>
        /// アイテムを損傷・損耗させる（武器・防具も含む） アイテムの場合、
        /// 損耗なのでマイナスするが 武器・防具は損傷度を表すのでプラスにする。
        /// </summary>
        /// <param name="objectId"></param>
        /// <returns></returns>
        public L1ItemInstance receiveDamage( int objectId )
        {
            L1ItemInstance item = getItem( objectId );
            return receiveDamage( item );
        }

        public L1ItemInstance receiveDamage( L1ItemInstance item )
        {
            return receiveDamage( item, 1 );
        }

        public L1ItemInstance receiveDamage( L1ItemInstance item, int count )
        {
            int itemType = item.getItem().getType2();
            int currentDurability = item.get_durability();

            if ( item == null )
            {
                return null;
            }

            if ( ( currentDurability == 0 && itemType == 0 ) || currentDurability < 0 )
            {
                item.set_durability( 0 );
                return null;
            }

            // 武器・防具のみ損傷度をプラス
            if ( itemType == 0 )
            {
                int minDurability = ( item.getEnchantLevel() + 5 ) * -1;
                int durability = currentDurability - count;
                if ( durability < minDurability )
                {
                    durability = minDurability;
                }
                if ( currentDurability > durability )
                {
                    item.set_durability( durability );
                }
            }
            else
            {
                int maxDurability = item.getEnchantLevel() + 5;
                int durability = currentDurability + count;
                if ( durability > maxDurability )
                {
                    durability = maxDurability;
                }
                if ( currentDurability < durability )
                {
                    item.set_durability( durability );
                }
            }

            updateItem( item, L1PcInventory.COL_DURABILITY );
            return item;
        }

        public L1ItemInstance recoveryDamage( L1ItemInstance item )
        {
            int itemType = item.getItem().getType2();
            int durability = item.get_durability();

            if ( item == null )
            {
                return null;
            }

            if ( ( durability == 0 && itemType != 0 ) || durability < 0 )
            {
                item.set_durability( 0 );
                return null;
            }

            if ( itemType == 0 )
            {
                // 耐久度をプラスしている。
                item.set_durability( durability + 1 );
            }
            else
            {
                // 損傷度をマイナスしている。
                item.set_durability( durability - 1 );
            }

            updateItem( item, L1PcInventory.COL_DURABILITY );
            return item;
        }

        /// <summary>
        /// アイテムＩＤから検索
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public L1ItemInstance findItemId( int id )
        {/*TTOK*/
            foreach ( L1ItemInstance item in _items )
            {
                if ( item.getItem().getItemId() == id )
                {
                    return item;
                }
            }
            return null;
        }

        public L1ItemInstance[] findItemsId( int id )
        {
            List<L1ItemInstance> itemList = new List<L1ItemInstance>();
            foreach ( L1ItemInstance item in _items )
            {
                if ( item.getItemId() == id )
                {
                    itemList.Add( item );
                }
            }
            return itemList.ToArray();
        }

        public L1ItemInstance[] findItemsIdNotEquipped( int id )
        {
            List<L1ItemInstance> itemList = new List<L1ItemInstance>();
            foreach ( L1ItemInstance item in _items )
            {
                if ( item.getItemId() == id )
                {
                    if ( !item.isEquipped() )
                    {
                        itemList.Add( item );
                    }
                }
            }
            return itemList.ToArray();
        }

        /// <summary>
        /// オブジェクトＩＤから検索
        /// </summary>
        /// <param name="objectId"></param>
        /// <returns></returns>
        public L1ItemInstance getItem( int objectId )
        {
            foreach ( Object itemObject in _items )
            {
                L1ItemInstance item = (L1ItemInstance)itemObject;
                if ( item.getId() == objectId )
                {
                    return item;
                }
            }
            return null;
        }

        /// <summary>
        /// 特定のアイテムを指定された個数以上所持しているか確認（矢とか魔石の確認）
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool checkItem( int id )
        {
            return checkItem( id, 1 );
        }

        public bool checkItem( int id, int count )
        {
            if ( count == 0 )
            {
                return true;
            }
            if ( ItemTable.getInstance().getTemplate( id ).isStackable() )
            {
                L1ItemInstance item = findItemId( id );
                if ( item != null && item.getCount() >= count )
                {
                    return true;
                }
            }
            else
            {
                Object[] itemList = findItemsId( id );
                if ( itemList.Length >= count )
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 強化された特定のアイテムを指定された個数以上所持しているか確認
        /// 装備中のアイテムは所持していないと判別する
        /// </summary>
        /// <param name="id"></param>
        /// <param name="enchant"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public bool checkEnchantItem( int id, int enchant, int count )
        {
            int num = 0;
            foreach ( L1ItemInstance item in _items )
            {
                if ( item.isEquipped() )
                { // 装備しているものは該当しない
                    continue;
                }
                if ( item.getItemId() == id && item.getEnchantLevel() == enchant )
                {
                    num++;
                    if ( num == count )
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 強化された特定のアイテムを消費する
        /// 装備中のアイテムは所持していないと判別する
        /// </summary>
        /// <param name="id"></param>
        /// <param name="enchant"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public bool consumeEnchantItem( int id, int enchant, int count )
        {
            foreach ( L1ItemInstance item in _items )
            {
                if ( item.isEquipped() )
                { // 装備しているものは該当しない
                    continue;
                }
                if ( item.getItemId() == id && item.getEnchantLevel() == enchant )
                {
                    removeItem( item );
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 特定のアイテムを指定された個数以上所持しているか確認
        /// 装備中のアイテムは所持していないと判別する
        /// </summary>
        /// <param name="id"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public bool checkItemNotEquipped( int id, int count )
        {
            if ( count == 0 )
            {
                return true;
            }
            return count <= countItems( id );
        }

        // 特定のアイテムを全て必要な個数所持しているか確認（イベントとかで複数のアイテムを所持しているか確認するため）
        public bool checkItem( int[] ids )
        {
            int len = ids.Length;
            int[] counts = new int[len];
            for ( int i = 0; i < len; i++ )
            {
                counts[i] = 1;
            }
            return checkItem( ids, counts );
        }

        public bool checkItem( int[] ids, int[] counts )
        {
            for ( int i = 0; i < ids.Length; i++ )
            {
                if ( !checkItem( ids[i], counts[i] ) )
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// このインベントリ内にある、指定されたIDのアイテムの数を数える。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int countItems( int id )
        {
            if ( ItemTable.getInstance().getTemplate( id ).isStackable() )
            {
                L1ItemInstance item = findItemId( id );
                if ( item != null )
                {
                    return item.getCount();
                }
            }
            else
            {
                Object[] itemList = findItemsIdNotEquipped( id );
                return itemList.Length;
            }
            return 0;
        }

        public void shuffle()
        {
            // TODO 優先度8 独自シャッフル
            //Collections.shuffle( _items );
            Random rand = new Random();
            for ( int i = 0; i < _items.Count; i++ )
            {
                int r = rand.Next( _items.Count );
                L1ItemInstance temp = (L1ItemInstance)_items[r];
                _items[r] = _items[i];
                _items[i] = temp;
            }
        }

        /// <summary>
        /// インベントリ内の全てのアイテムを消す（所有者を消すときなど）
        /// </summary>
        public void clearItems()
        {
            foreach ( Object itemObject in _items )
            {
                L1ItemInstance item = (L1ItemInstance)itemObject;
                L1World.getInstance().removeObject( item );
            }
            _items.Clear();
        }

        /// <summary>
        /// アイテムを読み込む。(virtualで処理はなし)
        /// </summary>
        public virtual void loadItems() { }

        /// <summary>
        /// アイテムを追加する。(virtualで処理はなし)
        /// </summary>
        /// <param name="item"></param>
        public virtual void insertItem( L1ItemInstance item ) { }

        /// <summary>
        /// アイテムを更新する。(virtualで処理はなし)
        /// </summary>
        /// <param name="item"></param>
        public virtual void updateItem( L1ItemInstance item ) { }

        /// <summary>
        /// アイテムを追加する。(virtualで処理はなし)
        /// </summary>
        /// <param name="item"></param>
        /// <param name="colmn"></param>
        public virtual void updateItem( L1ItemInstance item, int colmn ) { }
    }
}
